package graph_viewer.util;

import java.awt.*;
import java.util.*;

import graph_viewer.app_exception.*;
import graph_viewer.layout.*;
import graph_viewer.structure.*;

public class  Layout_Util {

	static public int get_thumbnail_x_margin(){
		return Layout.THUMBNAIL_X_MARGIN;
	}	

	static public int get_thumbnail_y_margin(){
		return Layout.THUMBNAIL_Y_MARGIN;
	}	
	
	static public  int find_overlapping_number( Graph a_graph ){
		ArrayList nodes	= Structure_Util.find_graph_root( a_graph ).get_children();
			//a_graph.find_nodes();
		
		Layout_Util.y_direction_sort( nodes );
		Layout_Util.x_direction_sort( nodes );
		
		int overlapping_number	= 0;

		int			num_of_nodes = nodes.size();//deduct the root;
		
		boolean		found_overlapped_node	= false;

		for ( int i = 0 ; i < num_of_nodes; i++ ){
			Node a_node	= (Node)nodes.get( i );

			for ( int j = i + 1; j < num_of_nodes; j++ ){
				Node another_node	= (Node)nodes.get( j );

				if ( a_node.is_overlapped( another_node ) ){
					overlapping_number++;

					//Debug.println( a_node + " overlapped " + another_node, 12);
				}

			}//end of for j
		}//end of for i

		return overlapping_number;	
	}

	static public void move_super_cluster( Graph a_graph, Node ancestor, Node_Position target_position ){
		Debug.println( "To move_super_cluster: " + ancestor, 53 );
		Node_Position source_position	= new Node_Position( ancestor.get_position() );
		ancestor.set_position( new Node_Position( target_position ) );

		move_descendants( a_graph, ancestor, source_position );
	}

	
	/**
		Move all the descendants of a cluster when this cluster moved to a new position. In order to the drawing of this cluster includ all its descendants.
		@param	ancestor				a cluster with the lastest position.
		@param	source_position		ancester's initial position, from where moves desc-node to current position
	*/
	static public void move_descendants( Graph a_graph, Node ancestor, Node_Position source_position ){
		if ( ancestor.get_is_closed() ){
			return;
		}

		//Debug.println( "=========Move descedants of " + ancestor, 33 );
		Node_Position moved_ancestor_position = ancestor.get_position();

		int []
			initial_center	= source_position.find_center(),
			moved_center	= moved_ancestor_position.find_center();

		int
			x_moved_distance	= moved_center[0] - initial_center[0],
			y_moved_distance	= moved_center[1] - initial_center[1];

		Debug.println( "\t" + ancestor + "\tmove to current center from " + initial_center[0] + ", " +  initial_center[1], 33 );
		//Debug.println( "\tinitial_center " + initial_center[0] + ", " + initial_center[1], 3 );

		Debug.println( "\tx_moved_distance " + x_moved_distance + "\ty_moved_distance " + y_moved_distance, 33 );
		
		ArrayList descedants	 = a_graph.find_tree().find_descedants( ancestor );

		Debug.println( ancestor + " Move descedants\n" + descedants, 33 );
		for ( Iterator _i = descedants.iterator() ; _i.hasNext(); ){
			Node desc							= (Node)_i.next();

			if ( !desc.is_displayed() ){
				continue;
			}
			
			Node_Position	desc_position	= desc.get_position();
			
			//Debug.println( "\tMove " + desc, 32 );

			Debug.println( "\t--before moving:, " + desc, 33 );
			int[] desc_initial_center			= desc_position.find_center();
			int
				desc_x_moved_center	= desc_initial_center[0] + x_moved_distance,
				desc_y_moved_center	= desc_initial_center[1] + y_moved_distance;

			int[] desc_moved_center	= new int[]{ desc_x_moved_center, desc_y_moved_center};
			desc_position.set_center( desc_moved_center );

			Debug.println( "\t--after moving:, " + desc, 33 );
		}		
		
		Debug.println( "\tFinish Move descedants of " + ancestor, 33 );
		/**/
	}

	/**
		move nodes from source_center to destine_center.
	*/
	static public void move_node_to_another_center( Graph a_graph, String a_node_label, int[] source_center, int[] destine_center ){
		Debug.println( "\t\tmove_node_to_another_center: " + a_node_label + "\t" + source_center[0] + ", " + source_center[1] + " to " + destine_center[0] + ", " + destine_center[1] , 32 );
		Node a_node	= a_graph.search( a_node_label );			
		a_node.get_position().set_center( destine_center );

		Node_Position	source_position = new Node_Position( source_center, a_node.get_position().get_width(), a_node.get_position().get_height() );
		
		Debug.println( "\t\t" + a_node + " move_descendants  : " + a_node.get_children(), 32 );

		move_descendants( a_graph, a_node, source_position );
	}

	static public void move_non_foucs_node( Graph graph, Node_Position focus_position_before_distortion, Node_Position focus_position_after_distortion, String focus_node_label ){

		int 
			curren_width		= focus_position_after_distortion.get_width(),
			current_height		= focus_position_after_distortion.get_height(),
			before_distortion_width		= focus_position_before_distortion.get_width(),
			before_distortion_height	= focus_position_before_distortion.get_height();

		//Debug.println( "move_non_foucs_node: " , 3 );
		//Debug.println( "curren_width: " + curren_width, 3 );
		//Debug.println( "current_height: " + current_height, 3 );
		//Debug.println( "before_distortion_width: " + before_distortion_width, 3 );
		//Debug.println( "before_distortion_height: " + before_distortion_height, 3 );
		int
			x_direction_enlarged_distance	= curren_width - before_distortion_width,
			y_direction_enlarged_distance	= current_height - before_distortion_height;

		//Debug.println( "x_direction_enlarged_distance: " + x_direction_enlarged_distance, 3 );
		//Debug.println( "y_direction_enlarged_distance: " + y_direction_enlarged_distance, 3 );
		
		//move first level clusters
		Tree	tree	= graph.find_tree();

		ArrayList nodes	= tree.get_root().get_children();
		Node focus_node	= graph.search( focus_node_label );
		
		for ( Iterator _i = nodes.iterator(); _i.hasNext(); ){
			Node node	= (Node)_i.next();

			if ( !tree.is_ancestor( node, focus_node) && node.get_label() != focus_node_label ){
				Node_Position	
					np	 = node.get_position(),
					np_cp	= new Node_Position( np ) ;

				if ( np.is_left( focus_position_before_distortion ) ){
					np.set_x_center( np.get_x_center() - x_direction_enlarged_distance/2 );
				}

				if ( np.is_right( focus_position_before_distortion ) ){
					np.set_x_center( np.get_x_center() + x_direction_enlarged_distance/2 );
				}

				if ( np.is_above( focus_position_before_distortion ) ){
					np.set_y_center( np.get_y_center() - y_direction_enlarged_distance/2 );
				}

				if ( np.is_below( focus_position_before_distortion ) ){
					np.set_y_center( np.get_y_center() + y_direction_enlarged_distance/2 );
				}

				move_descendants( graph, node, np_cp );
			}			
		}
	}

	static public double find_distance( Point current_point_position, Point neighbour_point_position ){
		double 
			current_point_x_center		= current_point_position.getX(),
			current_point_y_center		= current_point_position.getY(),
			neighbour_point_x_center	= neighbour_point_position.getX(),
			neighbour_point_y_center	= neighbour_point_position.getY(),

			points_x_distance = Math.abs( current_point_x_center - neighbour_point_x_center ),
			points_y_distance	 = Math.abs( current_point_y_center - neighbour_point_y_center );

		double 
			points_distance	= Math.sqrt( points_x_distance * points_x_distance + points_y_distance * points_y_distance);
		
		return Math.abs( points_distance );
	}

	static public double find_line_tangent( Point from_point, Point to_point ){

		double
			from_point_x_center		= (double)from_point.getX(),
			from_point_y_center		= (double)from_point.getY(),
			to_point_x_center	= (double)to_point.getX(),
			to_point_y_center	= (double)to_point.getY();

		double tangent	= Math.abs( from_point_y_center - to_point_y_center ) / Math.abs( from_point_x_center - to_point_x_center );

		return tangent;	
	}

	static public double find_nodes_distance( Node current_node, Node neighbour_node ){
		Debug.println( "\tfind_nodes_distance: " + current_node + " & " + neighbour_node, 43 );
		Node_Position
			current_node_position		= current_node.get_position(),
			neighbour_node_position	= neighbour_node.get_position();

		return Layout_Util.find_nodes_distance(	current_node_position, neighbour_node_position );
	}

	static public double find_nodes_distance( Node_Position current_node_position, Node_Position neighbour_node_position ){
		
		return Layout_Util.find_distance(	current_node_position.find_center_point(), neighbour_node_position.find_center_point() );
	}
	
	static public void x_direction_sort( ArrayList clusters ){		
		sort( clusters, Public_Values.X_AXIS );
	}

	static public void y_direction_sort( ArrayList clusters ){		
		sort( clusters, Public_Values.Y_AXIS );
	}

	//bubble sort
	//@param	clusters
	static public void sort( ArrayList clusters, int direction ){		
		
		int size	= clusters.size();

		for ( int i = 0 ; i < size; i++ ){
			Node	sorted_node	= (Node)clusters.get( i );
			
			for ( int j = i + 1; j < size; j++ ){
				Node other_node	= (Node)clusters.get( j );
				
				sorted_node	= min( sorted_node, other_node, direction );
			}//end of for j
			
			//sorted_clusters.add( sorted_node );
			Node sorted_node_cp = (Node)clusters.remove( clusters.indexOf(sorted_node)  );
			clusters.add(i , sorted_node_cp );
		}//end of for i

		//return clusters;//sorted_clusters;
	}
	
	static public void sort_by_label( Graph a_graph, ArrayList node_labels, int direction ){
		Debug.println( "sort_by_label: " + node_labels, 4 ) ;
		ArrayList nodes	= new ArrayList();

		for ( Iterator _i = node_labels.iterator() ; _i.hasNext(); ){
			String	label		= (String)_i.next();
			
			Node	a_node	= a_graph.search( label ) ;

			if ( a_node != null ){
				nodes.add( a_node );
			}
			
		}

		sort( nodes, direction );
	}



	//@return the node with smaller coordinate in the direction
	static private Node min( Node a_node, Node other_node, int direction ){
		double 
			a_node_coor	= get_compared_coordinate( a_node, direction ),
			other_node_coor	= get_compared_coordinate( other_node, direction );
		
		if ( a_node_coor > other_node_coor ){
			return other_node;
		}else{
			return a_node;
		}
	}

	static private double get_compared_coordinate( Node a_node, int direction ){
		Node_Position	np			= a_node.get_position();

		int[]				location	= np.find_center();//np.find_location();

		//Debug.println( "get_compared_coordinate of " + a_node , 4 );
		int compared_coordinate;
		if ( direction == Public_Values.X_AXIS ){
			compared_coordinate = location[0];
		}else if ( direction == Public_Values.Y_AXIS ){
			compared_coordinate = location[1];
		}else{
			throw new App_Exception( "Invalid direction value found when adjust layout!");
		}
	
		//Debug.println( "=========" + a_node , 4 );
		return compared_coordinate;
	}
	
	
 }//:~